// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
//
// A Cache is an interface that maps keys to values.  It has internal
// synchronization and may be safely accessed concurrently from
// multiple threads.  It may automatically evict entries to make room
// for new entries.  Values have a specified charge against the cache
// capacity.  For example, a cache where the values are variable
// length strings, may use the length of the string as the charge for
// the string.
//
// A builtin cache implementation with a least-recently-used eviction
// policy is provided.  Clients may use their own implementations if
// they want something more sophisticated (like scan-resistance, a
// custom eviction policy, variable cache sizing, etc.)

#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_
#define STORAGE_LEVELDB_INCLUDE_CACHE_H_

#include <cstdint>

#include "leveldb/export.h"
#include "leveldb/slice.h"

namespace leveldb {

class LEVELDB_EXPORT Cache;

// Create a new cache with a fixed size capacity.  This implementation
// of Cache uses a least-recently-used eviction policy.
LEVELDB_EXPORT Cache* NewLRUCache(size_t capacity);

class LEVELDB_EXPORT Cache {
 public:
  Cache() = default;

  Cache(const Cache&) = delete;
  Cache& operator=(const Cache&) = delete;

  // Destroys all existing entries by calling the "deleter"
  // function that was passed to the constructor.
  virtual ~Cache();

  // Opaque handle to an entry stored in the cache.
  // 该Handle用来指向Cache中的一个缓存项，即Handle是用户访问Cache中缓存项的一个凭证。
  struct Handle {};

  // Insert a mapping from key->value into the cache and assign it
  // the specified charge against the total cache capacity.
  //
  // Returns a handle that corresponds to the mapping.  The caller
  // must call this->Release(handle) when the returned mapping is no
  // longer needed.
  //
  // When the inserted entry is no longer needed, the key and
  // value will be passed to "deleter".
  // 插入一个键值对（key，value）到缓存（cache）中，并从缓存总容量中减去该键值对所占额度（charge）
  //
  // 返回指向该键值对的句柄（handle），调用者在用完句柄后，需要调用 this->Release(handle) 进行释放
  //
  // 在键值对不再被使用时，键值对会被传入的 deleter 参数释放
  /*
   * 参数key是Slice引用类型，value是任意类型指针，charge用来告知Cache该缓存项占用容量。
   * 显然，Cache不需要知道value具体占用多大空间，也无从得知其类型，这说明Cache的用户需要自己控制value的空间释放。
   * Insert方法的最后一个参数回调函数*deleter即用来释放value空间的方法（LevelDB内部实现的Cache会深拷贝key的数据，不需要用户释放）。
   */
  /*
   * 为了避免释放仍在使用的缓存项，同时提供线程安全地访问，缓存项的释放需要依赖引用计数。
   * 当用户更新了key相同的缓存或删除key相应的缓存时，Cache只会将其移出其管理结构，不会释放其内存空间。只有当其引用计数归零时才会通过之前传入的回调函数deleter释放。
   * 用户对缓存项引用计数的操作即通过Handle来实现。用户在通过Insert或LookUp方法得到缓存项的Handle时，缓存项的引用计数会+1。
   * 两个方法声明的注释部分指出，用户在不需要继续使用该缓存项时，需要调用Release方法并传入该缓存项的Handle。Release方法会使缓存项的引用计数-1。
   */
  virtual Handle* Insert(const Slice& key, void* value, size_t charge,
                         void (*deleter)(const Slice& key, void* value)) = 0;

  // 如果缓存中没有相应键（key），则返回 nullptr。
  //
  // 否则返回指向对应键值对的句柄（Handle）。调用者用完句柄后，要记得调用 this->Release(handle) 进行释放。
  virtual Handle* Lookup(const Slice& key) = 0;

  // 释放 Insert/Lookup 函数返回的句柄
  // 要求：该句柄没有被释放过，即不能多次释放
  // 要求：该句柄必须是同一个实例返回的
  virtual void Release(Handle* handle) = 0;

  // 获取句柄中的值，类型为 void*（表示任意用户自定义类型）
  // 要求：该句柄没有被释放
  // 要求：该句柄必须由同一实例所返回
  virtual void* Value(Handle* handle) = 0;

  // 如果缓存中包含给定键所指向的条目，则删除之。
  // 需要注意的是，只有在所有持有该条目句柄都释放时，该条目所占空间才会真正被释放
  virtual void Erase(const Slice& key) = 0;

  // 返回一个自增的数值id。当一个缓存实例由多个客户端共享时，为了避免多个客户端的键冲突，
  // 每个客户端可能想获取一个独有的id，并将其作为键的前缀。类似于给每个客户端一个单独的命名空间。
  virtual uint64_t NewId() = 0;

  // 驱逐全部没有被使用的数据条目
  // 内存吃紧型的应用可能想利用此接口定期释放内存。
  // 基类中的 Prune 默认实现为空，但强烈建议所有子类自行实现。
  // 将来的版本可能会增加一个默认实现。
  virtual void Prune() {}

  // 返回当前缓存中所有数据条目所占容量总和的一个预估
  virtual size_t TotalCharge() const = 0;
};

}  // namespace leveldb

#endif  // STORAGE_LEVELDB_INCLUDE_CACHE_H_
